// Package logging
// Title 			zap.logging.components.go
// Description 		日志处理组件
// Author  			alittlexincan@163.com 					2024/07/22 11:36
// Update  			alittlexincan@163.com 					2024/07/22 11:36
package logging

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/spf13/viper"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
	"xincan.com.cn/tarot-go/internal/common/constants"
)

// ANSI 颜色代码 (兼容Linux/Windows终端)
const (
	colorReset  = "\x1b[0m"
	colorRed    = "\x1b[31m"
	colorGreen  = "\x1b[32m"
	colorYellow = "\x1b[33m"
	colorBlue   = "\x1b[34m"
	colorPurple = "\x1b[35m"
)

// appConfig
// Title    			appConfig
// Description 			应用配置实例
// Author  				alittlexincan@163.com 					2024/07/22 11:36
// Update  				alittlexincan@163.com 					2024/07/22 11:36
type appConfig struct {
	active  string // 运行环境
	name    string // 系统名称
	version string // 系统版本
}

// loggerConfig
// Title    			loggerConfig
// Description 			日志配置实例
// Author  				alittlexincan@163.com 					2024/07/22 11:36
// Update  				alittlexincan@163.com 					2024/07/22 11:36
type loggerConfig struct {
	enabled    bool   // 是否开启日志存储, 默认: true
	path       string // 日志存储: 日志存放地址, 默认: ./logs
	maxBackups int    // 日志文件最多保存多少个备份(单位: 个), 默认: 10
	maxSize    int    // 日志文件在轮转前最大的大小(单位: MiB), 默认: 100MiB
	maxAge     int    // 基于文件名中编码的时间戳保留旧日志文件最大的天数。默认不根据时间删除旧日志文件。(单位: 天), 默认7天
	compress   bool   // 是否开启日志压缩,使用gzip压缩已旋转的日志文件, 默认:  false
}

// applicationLoggerConfig
// Title    			applicationLoggerConfig
// Description 			应用日志配置实例
// Author  				alittlexincan@163.com 					2024/07/22 11:36
// Update  				alittlexincan@163.com 					2024/07/22 11:36
type applicationLoggerConfig struct {
	app    *appConfig         // 应用配置实例
	config *loggerConfig      // 日志配置实例
	logger *TraceLogger       // 自定义日志包装器实例
	sugar  *zap.SugaredLogger // 简化日志配置实例
	once   sync.Once          // 线程安全
}

// ApplicationLoggerConfigBuilder
// Title 			ApplicationLoggerConfigBuilder
// Description 		日志配置构建器实例
// Author  			alittlexincan@163.com 					2024/07/22 11:36
// Update  			alittlexincan@163.com 					2024/07/22 11:36
// Return    		*ApplicationLoggerConfigBuilder    		返回日志配置构建器实例
type ApplicationLoggerConfigBuilder struct {
	*applicationLoggerConfig
}

// NewApplicationLoggerConfigBuilder
// Title 			NewApplicationLoggerConfigBuilder
// Description 		创建日志配置构建器实例
// Author  			alittlexincan@163.com 					2024/07/22 11:36
// Update  			alittlexincan@163.com 					2024/07/22 11:36
// Return    		*ApplicationLoggerConfigBuilder    		返回日志配置构建器实例
func NewApplicationLoggerConfigBuilder() *ApplicationLoggerConfigBuilder {
	return &ApplicationLoggerConfigBuilder{
		&applicationLoggerConfig{},
	}
}

// WithLocalViper
// Title 			WithLocalViper
// Description 		构造器: 使用本地配置文件初始化日志配置
// Author  			alittlexincan@163.com 					2024/07/22 11:36
// Update  			alittlexincan@163.com 					2024/07/22 11:36
// Param    		viper *viper.Viper 						本地配置实例
// Return    		*ApplicationLoggerConfigBuilder    		返回日志配置构建器实例
func (builder *ApplicationLoggerConfigBuilder) WithLocalViper(viper *viper.Viper) *ApplicationLoggerConfigBuilder {

	if viper != nil {
		builder.app = &appConfig{
			active:  viper.GetString("application.active"),
			name:    viper.GetString("application.name"),
			version: viper.GetString("application.version"),
		}
		builder.config = &loggerConfig{
			enabled:    viper.GetBool("application.log.enabled"),
			path:       viper.GetString("application.log.path"),
			maxBackups: viper.GetInt("application.log.maxBackups"),
			maxSize:    viper.GetInt("application.log.maxSize"),
			maxAge:     viper.GetInt("application.log.maxAge"),
			compress:   viper.GetBool("application.log.compress"),
		}
	}

	return builder
}

// Build
// Title    			Build
// Description   		构建日志配置
// Author      			alittlexincan@163.com       			2024/07/17 11:36
// Update    			alittlexincan@163.com       			2024/07/17 11:36
// Return    			*viper.Viper							yaml解析器实例, viper
// Return 				*zap.SugaredLogger     					yaml解析器实例, sugar
func (builder *ApplicationLoggerConfigBuilder) Build() (*TraceLogger, *zap.SugaredLogger) {
	return builder.loggerLoad()
}

// loggerLoad
// Title    			loggerLoad
// Description   		初始化zap日志配置
// Author      			alittlexincan@163.com       			2024/07/17 11:36
// Update    			alittlexincan@163.com       			2024/07/17 11:36
// Return    			*viper.Viper							yaml解析器实例, viper
// Return 				*zap.SugaredLogger     					yaml解析器实例, sugar
func (logger *applicationLoggerConfig) loggerLoad() (*TraceLogger, *zap.SugaredLogger) {

	// 1: 判断日志是否启用
	if !logger.config.enabled {
		log.Println("日志未启用")
		return nil, nil
	}
	// 2: 初始化日志配置
	logger.once.Do(func() {
		// 动态日志级别（支持运行时热更新）
		logLevel := zapcore.InfoLevel
		if logger.app.active == "dev" {
			logLevel = zapcore.DebugLevel
		}
		atomicLevel := zap.NewAtomicLevelAt(logLevel)
		// 核心配置
		zc := zapcore.NewTee(
			logger.createFileCore("info", atomicLevel, zapcore.InfoLevel, false), // 文件日志无颜色
			logger.createFileCore("error", atomicLevel, zapcore.ErrorLevel, false),
			logger.createConsoleCore(atomicLevel), // 控制台日志带颜色
		)
		// 构建Logger（增加堆栈跟踪和调用方信息）
		zapLogger := zap.New(zc,
			zap.AddCaller(),
			zap.AddCallerSkip(1),
			zap.AddStacktrace(zapcore.ErrorLevel),
		).With(
		//zap.String("service", logger.app.name),
		//zap.String("version", logger.app.version),
		)
		logger.logger = &TraceLogger{Logger: zapLogger}
		logger.sugar = zapLogger.Sugar()
	})
	logger.logger.Info("加载日志配置成功")
	return logger.logger, logger.sugar
}

// colorEncodeLevel
// Title    			colorEncodeLevel
// Description   		彩色日志级别编码器
// Author      			alittlexincan@163.com       			2024/07/17 11:36
// Update    			alittlexincan@163.com       			2024/07/17 11:36
// Param    			level zapcore.Level						日志级别
// Param    			enc zapcore.PrimitiveArrayEncoder		编码器实例
func (logger *applicationLoggerConfig) colorEncodeLevel(level zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
	var color string
	switch level {
	case zapcore.DebugLevel:
		color = colorPurple
	case zapcore.InfoLevel:
		color = colorGreen
	case zapcore.WarnLevel:
		color = colorYellow
	case zapcore.ErrorLevel, zapcore.DPanicLevel, zapcore.PanicLevel, zapcore.FatalLevel:
		color = colorRed
	default:
		color = colorReset
	}
	enc.AppendString(color + level.CapitalString() + colorReset)
}

// createConsoleCore
// Title    			createConsoleCore
// Description   		创建控制台核心（带彩色输出）
// Author      			alittlexincan@163.com       			2024/07/17 11:36
// Update    			alittlexincan@163.com       			2024/07/17 11:36
// Param    			level zap.AtomicLevel					日志级别
// Return    			zapcore.Core     						日志核心实例
func (logger *applicationLoggerConfig) createConsoleCore(level zap.AtomicLevel) zapcore.Core {
	// 启用彩色输出
	encoderConfig := logger.getEncoderConfig(true)
	consoleEncoder := zapcore.NewConsoleEncoder(encoderConfig)
	// 加锁保证并发安全
	consoleOutput := zapcore.Lock(os.Stdout)
	return zapcore.NewCore(consoleEncoder, consoleOutput, level)
}

// createFileCore
// Title    			createFileCore
// Description   		创建日志核心（文件输出）
// Author      			alittlexincan@163.com       			2024/07/17 11:36
// Update    			alittlexincan@163.com       			2024/07/17 11:36
// Param    			application *tarot.Application			应用配置实例
// Param    			logType string							日志类型
// Param    			level zap.AtomicLevel					日志级别
// Param    			minLevel zapcore.Level					最小日志级别
// Return    			zapcore.Core     						日志核心实例
func (logger *applicationLoggerConfig) createFileCore(logType string, level zap.AtomicLevel, minLevel zapcore.Level, colorEnabled bool) zapcore.Core {

	config := logger.config

	// 日志切割配置
	lumberJack := &lumberjack.Logger{
		Filename:   logger.getLogPath(logType),
		MaxSize:    config.maxSize,
		MaxBackups: config.maxBackups,
		MaxAge:     config.maxAge,
		Compress:   config.compress,
		LocalTime:  true,
	}
	// 异步缓冲写入,提升性能,默认4KB缓冲区
	asyncWriter := &zapcore.BufferedWriteSyncer{WS: zapcore.AddSync(lumberJack), Size: 4096}
	// 文件日志使用JSON编码（无颜色）
	fileEncoder := zapcore.NewJSONEncoder(logger.getEncoderConfig(colorEnabled))
	return zapcore.NewCore(
		fileEncoder,
		asyncWriter,
		zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
			return lvl >= minLevel && lvl >= level.Level()
		}),
	)
}

// getEncoderConfig
// Title    			getEncoderConfig
// Description   		获取日志编码器配置
// Author      			alittlexincan@163.com       			2024/07/17 11:36
// Update    			alittlexincan@163.com       			2024/07/17 11:36
// Param    			colorEnabled bool						是否启用颜色
// Return    			zapcore.EncoderConfig     				日志编码器配置
func (logger *applicationLoggerConfig) getEncoderConfig(colorEnabled bool) zapcore.EncoderConfig {
	encoder := zapcore.EncoderConfig{
		TimeKey:        "timestamp",
		LevelKey:       "level",
		NameKey:        "logger",
		CallerKey:      "caller",
		MessageKey:     "msg",
		StacktraceKey:  "stacktrace",
		LineEnding:     zapcore.DefaultLineEnding,
		EncodeLevel:    zapcore.LowercaseColorLevelEncoder,
		EncodeTime:     zapcore.ISO8601TimeEncoder,
		EncodeDuration: zapcore.MillisDurationEncoder,
		EncodeCaller:   zapcore.ShortCallerEncoder,
	}
	// 动态设置级别编码器
	if colorEnabled {
		encoder.EncodeLevel = logger.colorEncodeLevel // 彩色编码
	} else {
		encoder.EncodeLevel = zapcore.LowercaseLevelEncoder // 无颜色
	}
	return encoder
}

// getLogPath
// Title    			getLogPath
// Description   		获取日志文件路径
// Author      			alittlexincan@163.com       			2024/07/17 11:36
// Update    			alittlexincan@163.com       			2024/07/17 11:36
// Param    			application *core.Application			应用配置实例
// Param    			logType string							日志类型
// Return    			string     								日志文件路径
func (logger *applicationLoggerConfig) getLogPath(logType string) string {
	logDir := logger.config.path
	active := logger.app.active
	if logDir == "" {
		logDir = constants.DefaultLogsDir
	}
	var filename string
	// 添加时间戳到日志文件名
	now := time.Now()
	dateStr := now.Format("2006-01-02")
	// 构造带日期的日志文件名
	if active == "" {
		filename = fmt.Sprintf("%s-%s.log", logType, dateStr)
	} else {
		filename = fmt.Sprintf("%s-%s-%s.log", logType, active, dateStr)
	}
	return filepath.Join(logDir, logger.app.name, filename)
}

// Sync
// Title    			Sync
// Description   		同步日志
// Author      			alittlexincan@163.com       			2024/07/17 11:36
// Update    			alittlexincan@163.com       			2024/07/17 11:36
// Return    			error     								错误信息
func Sync(logger *TraceLogger) error {
	if logger == nil {
		return nil
	}
	defer func() {
		if r := recover(); r != nil {
			logger.Error("日志同步发生错误", zap.Error(fmt.Errorf("%v", r)))
		}
	}()
	// 使用超时控制，避免同步阻塞
	done := make(chan bool, 1)
	var syncErr error

	go func() {
		syncErr = logger.Sync()
		done <- true
	}()
	select {
	case <-done:
		logger.Info("日志同步完成")
		return syncErr
	case <-time.After(5 * time.Second):
		logger.Error("日志同步超时，可能丢失部分日志")
		return fmt.Errorf("日志同步超时")
	}
}
